/**
 * Copyright © 2016-2025 The Thingsboard Authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.terracloud.server.service.log;

import com.terracloud.server.common.data.id.DeviceId;
import com.terracloud.server.common.data.id.TenantId;
import com.terracloud.server.common.data.id.UserId;
import com.terracloud.server.common.data.log.LogLevel;
import com.terracloud.server.common.data.log.LogType;
import com.terracloud.server.common.data.log.TerminalLog;
import com.terracloud.server.common.data.log.PlatformLog;
import com.terracloud.server.common.data.log.LogStatus;
import com.terracloud.server.common.data.id.TerminalLogId;
import com.terracloud.server.common.data.id.PlatformLogId;
import com.terracloud.server.config.SpringContextHolder;
import com.terracloud.server.service.security.model.SecurityUser;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import jakarta.servlet.http.HttpServletRequest;
import java.util.UUID;

/**
 * 统一日志工具类
 * 提供简单易用的日志记录方法，开发人员可以通过一行代码记录所有类型的日志
 */
@Component
@Slf4j
public class UnifiedLogUtil {
    
    @Setter(onMethod_ = @Autowired)
    private static UnifiedLogService unifiedLogService;
    
    // 私有构造函数，防止实例化
    private UnifiedLogUtil() {
    }
    
    /**
     * 获取UnifiedLogService实例
     * 
     * @return UnifiedLogService实例
     */
    private static UnifiedLogService getUnifiedLogService() {
        if (unifiedLogService != null) {
            return unifiedLogService;
        }
        
        // 尝试从Spring上下文获取
        try {
            return SpringContextHolder.getBean(UnifiedLogService.class);
        } catch (Exception e) {
            log.warn("Failed to get UnifiedLogService from Spring context: {}", e.getMessage());
            return null;
        }
    }
    
    /**
     * 获取当前安全用户信息
     * 
     * @return SecurityUser 当前安全用户，如果无法获取则返回null
     */
    private static SecurityUser getCurrentUser() {
        try {
            var authentication = SecurityContextHolder.getContext().getAuthentication();
            if (authentication != null && authentication.getPrincipal() instanceof SecurityUser) {
                return (SecurityUser) authentication.getPrincipal();
            }
        } catch (Exception e) {
            log.warn("Failed to get current user from security context: {}", e.getMessage());
        }
        return null;
    }
    
    /**
     * 获取当前租户ID
     * 
     * @return TenantId 当前租户ID，如果无法获取则返回系统租户ID
     */
    private static TenantId getCurrentTenantId() {
        SecurityUser user = getCurrentUser();
        if (user != null) {
            return user.getTenantId();
        }
        return TenantId.SYS_TENANT_ID;
    }
    
    /**
     * 获取当前用户ID
     * 
     * @return UserId 当前用户ID，如果无法获取则返回null
     */
    private static UserId getCurrentUserId() {
        SecurityUser user = getCurrentUser();
        if (user != null) {
            return user.getId();
        }
        return null;
    }
    
    /**
     * 获取当前用户名
     * 
     * @return String 当前用户名，如果无法获取则返回"Unknown"
     */
    private static String getCurrentUserName() {
        SecurityUser user = getCurrentUser();
        if (user != null) {
            return user.getEmail();
        }
        return "Unknown";
    }
    
    /**
     * 获取当前请求的客户端IP地址
     * 
     * @return String 客户端IP地址，如果无法获取则返回"Unknown"
     */
    private static String getCurrentClientIpAddress() {
        try {
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            if (attributes != null) {
                HttpServletRequest request = attributes.getRequest();
                String xForwardedFor = request.getHeader("X-Forwarded-For");
                if (xForwardedFor != null && !xForwardedFor.isEmpty() && !"unknown".equalsIgnoreCase(xForwardedFor)) {
                    // X-Forwarded-For可能包含多个IP地址，取第一个
                    int index = xForwardedFor.indexOf(',');
                    if (index != -1) {
                        return xForwardedFor.substring(0, index);
                    } else {
                        return xForwardedFor;
                    }
                }
                
                String xRealIp = request.getHeader("X-Real-IP");
                if (xRealIp != null && !xRealIp.isEmpty() && !"unknown".equalsIgnoreCase(xRealIp)) {
                    return xRealIp;
                }
                
                return request.getRemoteAddr();
            }
        } catch (Exception e) {
            log.warn("Failed to get client IP address: {}", e.getMessage());
        }
        return "Unknown";
    }
    
    /**
     * 记录终端日志
     * 
     * @param deviceId 设备ID
     * @param deviceName 设备名称
     * @param logType 日志类型
     * @param logLevel 日志级别
     * @param contentSummary 内容摘要
     * @param contentDetails 内容详情（可选）
     * @param processor 处理人（可选）
     * @param processStatus 处理状态（可选）
     * @param slaStatus SLA状态（可选）
     * @param slaDeadline SLA截止时间（可选）
     * @param duration 持续时间（可选）
     * @param isCritical 是否关键日志（可选）
     */
    public static void logTerminal(DeviceId deviceId, String deviceName, LogType logType, LogLevel logLevel,
                                  String contentSummary, String contentDetails, 
                                  String processor, LogStatus processStatus, String slaStatus, 
                                  Long slaDeadline, Long duration, Boolean isCritical) {
        TerminalLog terminalLog = new TerminalLog();
        terminalLog.setId(new TerminalLogId(UUID.randomUUID()));
        terminalLog.setCreatedTime(System.currentTimeMillis());
        terminalLog.setDeviceId(deviceId);
        terminalLog.setDeviceName(deviceName);
        terminalLog.setLogType(logType);
        terminalLog.setLogLevel(logLevel);
        terminalLog.setContentSummary(contentSummary);
        terminalLog.setContentDetails(contentDetails);
        terminalLog.setOccurTime(System.currentTimeMillis());
        terminalLog.setTenantId(getCurrentTenantId());
        terminalLog.setProcessor(processor);
        terminalLog.setProcessStatus(processStatus);
        terminalLog.setSlaStatus(slaStatus);
        terminalLog.setSlaDeadline(slaDeadline);
        terminalLog.setDuration(duration);
        terminalLog.setIsCritical(isCritical);
        
        // 记录日志到SLF4J
        String logMessage = String.format("[TerminalLog] DeviceId: %s, Type: %s, Level: %s, TenantId: %s, UserId: %s, Content: %s",
                terminalLog.getDeviceId(), terminalLog.getLogType(), terminalLog.getLogLevel(), 
                terminalLog.getTenantId(), terminalLog.getProcessor(), terminalLog.getContentSummary());
        log.info(logMessage);
        
        // 实际调用服务层保存日志到数据库
        try {
            UnifiedLogService service = getUnifiedLogService();
            if (service != null) {
                service.logTerminal(terminalLog);
            } else {
                log.warn("UnifiedLogService is not available. Terminal log will not be saved to database.");
            }
        } catch (Exception e) {
            log.error("Failed to save terminal log to database: {}", e.getMessage(), e);
        }
    }
    
    /**
     * 记录平台日志
     * 
     * @param logType 日志类型
     * @param logLevel 日志级别
     * @param operationContent 操作内容
     * @param operationDetails 操作详情（可选）
     * @param businessId 业务ID（可选）
     * @param operationParams 操作参数（可选）
     * @param statusTransition 状态转换（可选）
     * @param exceptionInfo 异常信息（可选）
     * @param ipAddress IP地址（可选，如果不提供则自动获取）
     * @param status 状态（可选）
     */
    public static void logPlatform(LogType logType, LogLevel logLevel, String operationContent, 
                                  String operationDetails, String businessId, String operationParams,
                                  String statusTransition, String exceptionInfo, String ipAddress, String status) {
        PlatformLog platformLog = new PlatformLog();
        platformLog.setId(new PlatformLogId(UUID.randomUUID()));
        platformLog.setCreatedTime(System.currentTimeMillis());
        platformLog.setLogType(logType);
        platformLog.setLogLevel(logLevel);
        platformLog.setOperationContent(operationContent);
        platformLog.setOperationDetails(operationDetails);
        platformLog.setTenantId(getCurrentTenantId());
        platformLog.setUserId(getCurrentUserId());
        platformLog.setUserName(getCurrentUserName());
        platformLog.setBusinessId(businessId);
        platformLog.setOperationParams(operationParams);
        platformLog.setStatusTransition(statusTransition);
        platformLog.setExceptionInfo(exceptionInfo);
        platformLog.setOccurTime(System.currentTimeMillis());
        // 如果未提供IP地址，则自动获取当前客户端IP地址
        platformLog.setIpAddress(ipAddress != null ? ipAddress : getCurrentClientIpAddress());
        platformLog.setStatus(status);
        
        // 记录日志到SLF4J
        String logMessage = String.format("[PlatformLog] Type: %s, Level: %s, TenantId: %s, UserId: %s, Content: %s",
                platformLog.getLogType(), platformLog.getLogLevel(), platformLog.getTenantId(), 
                platformLog.getUserId(), platformLog.getOperationContent());
        log.info(logMessage);
        
        // 实际调用服务层保存日志到数据库
        try {
            UnifiedLogService service = getUnifiedLogService();
            if (service != null) {
                service.logPlatform(platformLog);
            } else {
                log.warn("UnifiedLogService is not available. Platform log will not be saved to database.");
            }
        } catch (Exception e) {
            log.error("Failed to save platform log to database: {}", e.getMessage(), e);
        }
    }
}